home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Info-Mac 4
/
Info_Mac IV CD-ROM (Pacific HiTech Inc.)(August 1994).iso
/
Development
/
Source
/
DBL Pascal Library
/
TransactionMgr
/
Transactions
Wrap
Text File
|
1993-04-14
|
10KB
|
292 lines
unit Transactions;
{The Transactions unit provides a framework for combining groups of operations on related}
{data into a single transaction. The transaction may be committed, cancelled, undone and}
{redone using a standard interface to a user-supplied routine that implements the semantics}
{of these transaction operations in terms of the user data and operations.}
{}
{This unit does not impose a particular semantics on transactions — semantics are the domain}
{of a user-supplied action routine. This framework can support both "update-on-commit" and}
{"retract-on-cancel" semantics. Update-on-commit semantics implies that any data created}
{during a transaction is not committed to the “live” database until the transaction is committed.}
{Retract-on-cancel means that the “live” database is modified during the transaction, and}
{restored to its pre-transaction state if the transaction is cancelled. You may also implement}
{a mixed strategy. For example, you could use "retract-on-cancel" for added and changed}
{records, but "update-on-commit" for deletions.}
interface
type
TransactionHandle = Handle;
TransactionOp = ( {}
transactionOpCommit, {}
transactionOpCancel, {}
transactionOpUndo, {}
transactionOpRedo, {}
transactionOpDispose {}
);
{NewTransaction creates a new transaction record. The record is valid until DisposeTransaction.}
{The transaction record may be reused for multiple transactions; it is initialized by each call to}
{BeginTransaction.}
function NewTransaction: TransactionHandle;
{BeginTransaction initializes the given transaction record for a new transaction, and records}
{the user’s actionProc which will be called to process entries on behalf of EndTransaction,}
{UndoTransaction, and RedoTransaction. The user’s actionProc is declared as follows:}
{ procedure ActionProc (userData, userAction: Longint; operation: TransactionOp; cancelled, undone: Boolean); }
procedure BeginTransaction (transaction: TransactionHandle;
actionProc: ProcPtr);
{RecordTransactionStep remembers one step of the current transaction. A step is represented}
{by a datum and an action. Any memory needed to store information about the step must be}
{allocated by the caller, and disposed by the user action proc when called with the dispose op.}
function RecordTransactionStep (transaction: TransactionHandle;
transData, actionCode: univ Longint): OSErr;
{TransactionExists searches the transaction record and returns true if a matched transaction is}
{found. Match criteria is established by a user supplied function. The match function must not}
{have side effects on the transaction, userData or userAction. The search begins with the most}
{recent transaction step if fromEnd is true.}
function TransactionExists (transaction: TransactionHandle;
fromEnd: Boolean;
function MatchFunc (userData, userAction: Longint): Boolean): Boolean;
{EndTransaction marks the end of all steps which comprise the current transaction. The}
{transaction may be committed or not — the action proc is called with the appropriate op code.}
{The action proc is called on step data in the same order as recorded for a commit, and in the}
{reverse order for a cancel. A cancelled transaction becomes non-undoable, even if it was}
{specified as undoable.}
procedure EndTransaction (transaction: TransactionHandle;
commit: Boolean);
{TransactionCancelled returns true if the transaction was cancelled (not committed).}
function TransactionCancelled (transaction: TransactionHandle): Boolean;
{SetTransactionUndoable specifies whether a transaction is undoable. Newly-created}
{transactions are not undoable. The state may be changed at any time.}
procedure SetTransactionUndoable (transaction: TransactionHandle;
undoable: Boolean);
{TransactionUndoable returns true if the transaction is undoable (or redoable).}
{The result is the same even if the transaction has been undone.}
function TransactionUndoable (transaction: TransactionHandle): Boolean;
{If the transaction is undoable, and has not previously been undone, UndoTransaction calls the}
{action proc using the undo op code with the step information in forward order.}
procedure UndoTransaction (transaction: TransactionHandle);
{TransactionUndone returns True if the transaction has been undone.}
function TransactionUndone (transaction: TransactionHandle): Boolean;
{If the transaction is undoable, and has been undone, RedoTransaction calls the}
{action proc using the redo op code with the step information in reverse order.}
procedure RedoTransaction (transaction: TransactionHandle);
{DisposeTransaction calls the action proc using the dispose op code with the step information}
{in forward order, then disposes of the transaction record and sets it to nil.}
procedure DisposeTransaction (var transaction: TransactionHandle);
implementation
type
TransactionEntry = record
userData: Longint;
userAction: Longint;
end;
TransactionRecHeader = record
userProc: ProcPtr;
count: Integer;
canUndo, undone, cancelled: Boolean;
end;
TransactionRec = record
header: TransactionRecHeader;
entries: array[1..1] of TransactionEntry;
end;
TransactionRecPtr = ^TransactionRec;
TransactionRecHdl = ^TransactionRecPtr;
function NewTransaction: TransactionHandle;
begin
NewTransaction := NewHandleClear(SIZEOF(TransactionRecHeader));
end;
procedure BeginTransaction (transaction: TransactionHandle;
actionProc: ProcPtr);
begin
with TransactionRecHdl(transaction)^^.header do
begin
userProc := actionProc;
count := 0;
canUndo := False;
undone := False;
cancelled := False;
end;
SetHandleSize(Handle(transaction), SIZEOF(TransactionRecHeader));
end;
procedure CallActionProc (userData, userAction: Longint;
operation: TransactionOp;
cancelled, undone: Boolean;
actionProc: ProcPtr);
inline
$205F, { move.l (a7)+,a0}
$4E90; { jsr (a0)}
type
OpDirection = (firstToLast, lastToFirst);
procedure ProcessTransactionOp (transaction: TransactionHandle;
operation: TransactionOp;
direction: OpDirection);
var
i: Integer;
begin
HLock(transaction);
with TransactionRecHdl(transaction)^^, header do
if direction = lastToFirst then
for i := count downto 1 do
{$PUSH}
{$R-}
with entries[i] do
{$POP}
CallActionProc(userData, userAction, operation, cancelled, undone, userProc)
else
for i := 1 to count do
{$PUSH}
{$R-}
with entries[i] do
{$POP}
CallActionProc(userData, userAction, operation, cancelled, undone, userProc);
HUnlock(transaction);
end;
function TransactionExists (transaction: TransactionHandle;
fromEnd: Boolean;
function MatchFunc (userData, userAction: Longint): Boolean): Boolean;
var
i: Integer;
result: Boolean;
begin
result := False;
HLock(Handle(transaction));
with TransactionRecHdl(transaction)^^, header do
if fromEnd then
begin
for i := count downto 1 do
{$PUSH}
{$R-}
with entries[i] do
{$POP}
if MatchFunc(userData, userAction) then
begin
result := True;
Leave;
end;
end
else
begin
for i := 1 to count do
{$PUSH}
{$R-}
with entries[i] do
{$POP}
if MatchFunc(userData, userAction) then
begin
result := True;
Leave;
end;
end;
HUnlock(Handle(transaction));
TransactionExists := result;
end;
procedure EndTransaction (transaction: TransactionHandle;
commit: Boolean);
begin
if commit then
ProcessTransactionOp(transaction, transactionOpCommit, firstToLast)
else
begin
ProcessTransactionOp(transaction, transactionOpCancel, lastToFirst);
SetTransactionUndoable(transaction, False);
TransactionRecHdl(transaction)^^.header.cancelled := True;
end;
end;
function TransactionCancelled (transaction: TransactionHandle): Boolean;
begin
TransactionCancelled := TransactionRecHdl(transaction)^^.header.cancelled;
end;
procedure SetTransactionUndoable (transaction: TransactionHandle;
undoable: Boolean);
begin
with TransactionRecHdl(transaction)^^.header do
if not cancelled then
canUndo := undoable;
end;
function TransactionUndoable (transaction: TransactionHandle): Boolean;
begin
TransactionUndoable := TransactionRecHdl(transaction)^^.header.canUndo;
end;
procedure UndoTransaction (transaction: TransactionHandle);
begin
with TransactionRecHdl(transaction)^^.header do
if canUndo and not undone then
begin
ProcessTransactionOp(transaction, transactionOpUndo, lastToFirst);
TransactionRecHdl(transaction)^^.header.undone := True;
end;
end;
function TransactionUndone (transaction: TransactionHandle): Boolean;
begin
TransactionUndone := TransactionRecHdl(transaction)^^.header.undone;
end;
procedure RedoTransaction (transaction: TransactionHandle);
begin
with TransactionRecHdl(transaction)^^.header do
if canUndo and undone then
begin
ProcessTransactionOp(transaction, transactionOpRedo, firstToLast);
TransactionRecHdl(transaction)^^.header.undone := False;
end;
end;
procedure DisposeTransaction (var transaction: TransactionHandle);
begin
ProcessTransactionOp(transaction, transactionOpDispose, lastToFirst);
DisposeHandle(Handle(transaction));
transaction := nil;
end;
function RecordTransactionStep (transaction: TransactionHandle;
transData, actionCode: univ Longint): OSErr;
begin
SetHandleSize(Handle(transaction), GetHandleSize(Handle(transaction)) + SIZEOF(TransactionEntry));
if MemError <> noErr then
begin
RecordTransactionStep := MemError;
Exit(RecordTransactionStep);
end
else
with TransactionRecHdl(transaction)^^, header do
if userProc <> nil then
begin
count := count + 1;
{$PUSH}
{$R-}
with entries[count] do
{$POP}
begin
userData := transData;
userAction := actionCode;
end;
end;
end;
end.